package main

import (
	"crypto/tls"
	"encoding/json"
	"flag"
	"fmt"
	"log"
	"os"
	"os/signal"
	"path/filepath"
	"strings"

	"gopkg.in/gomail.v2"
)

// 新增：文件大小阈值（超过此值则作为附件发送，默认10KB）
const maxBodyFileSize = 10 * 1024 // 10KB

// EmailConfig holds SMTP server and sender credentials
type EmailConfig struct {
	SenderEmail string `json:"sender_email"`
	SenderKey   string `json:"sender_key"`
	SMTPServer  string `json:"smtp_server"`
	SMTPPort    int    `json:"smtp_port"`
}

// defaultConfig is embedded fallback configuration
var defaultConfig = EmailConfig{
	SenderEmail: "1690544550@qq.com",
	SenderKey:   "znxogkkkexpbeeaj",
	SMTPServer:  "smtp.qq.com",
	SMTPPort:    465,
}

// multiRecipients handles multiple -mt flag values
type multiRecipients []string

func (m *multiRecipients) Set(value string) error {
	*m = append(*m, strings.TrimSpace(value))
	return nil
}

func (m *multiRecipients) String() string {
	return strings.Join(*m, ", ")
}

// LoadEmailConfig loads config from file/env or uses default
func LoadEmailConfig() (EmailConfig, error) {
	configPath := getConfigPath()
	if _, err := os.Stat(configPath); os.IsNotExist(err) {
		return defaultConfig, nil
	}

	content, err := os.ReadFile(configPath)
	if err != nil {
		return EmailConfig{}, err
	}

	var cfg EmailConfig
	if err := json.Unmarshal(content, &cfg); err != nil {
		return EmailConfig{}, err
	}

	if cfg.SenderEmail == "" {
		cfg.SenderEmail = defaultConfig.SenderEmail
	}
	if cfg.SenderKey == "" {
		cfg.SenderKey = defaultConfig.SenderKey
	}
	if cfg.SMTPServer == "" {
		cfg.SMTPServer = defaultConfig.SMTPServer
	}
	if cfg.SMTPPort == 0 {
		cfg.SMTPPort = defaultConfig.SMTPPort
	}

	return cfg, nil
}

// getConfigPath returns priority-based config file path
func getConfigPath() string {
	if envPath := os.Getenv("EMAIL_CONFIG_PATH"); envPath != "" {
		return envPath
	}

	homeDir, err := os.UserHomeDir()
	if err != nil {
		return "email_config.json"
	}

	return filepath.Join(homeDir, ".email_config.json")
}

// 修改：增加attachment参数，支持发送附件
func SendEmail(toUserEmail, subject, content, attachment string) error {
	cfg, err := LoadEmailConfig()
	if err != nil {
		log.Printf("加载配置失败: %v", err)
		return err
	}

	m := gomail.NewMessage()
	m.SetHeader("From", cfg.SenderEmail)
	m.SetHeader("To", toUserEmail)
	m.SetHeader("Subject", subject)

	// 设置邮件正文
	if strings.Contains(content, "<html") || strings.Contains(content, "<body") {
		m.SetBody("text/html", content)
	} else {
		m.SetBody("text/plain", content)
	}

	// 如果有附件则添加
	if attachment != "" {
		if _, err := os.Stat(attachment); err != nil {
			return fmt.Errorf("附件文件不存在: %w", err)
		}
		m.Attach(attachment)
	}

	d := gomail.NewDialer(
		cfg.SMTPServer,
		cfg.SMTPPort,
		cfg.SenderEmail,
		cfg.SenderKey,
	)

	d.TLSConfig = &tls.Config{
		InsecureSkipVerify: true,
		ServerName:         cfg.SMTPServer,
	}

	if err := d.DialAndSend(m); err != nil {
		log.Printf("发送失败: %v", err)
		return err
	}

	return nil
}

// setupInterruptIgnore ignores SIGINT (Ctrl+C)
func setupInterruptIgnore() {
	sigChan := make(chan os.Signal, 1)
	signal.Notify(sigChan, os.Interrupt)
	go func() {
		<-sigChan
		log.Println("已忽略中断信号")
	}()
}

// validateRequiredFlags checks mandatory parameters
func validateRequiredFlags(to string, fromFile, msgText string, mt multiRecipients, mtf string) error {
	if to == "" && len(mt) == 0 && mtf == "" {
		return fmt.Errorf("必须通过 -t/-mt/-mtf 指定至少一个收件人")
	}

	if fromFile == "" && msgText == "" {
		return fmt.Errorf("必须通过 -f/-m 指定邮件内容")
	}

	if fromFile != "" && msgText != "" {
		return fmt.Errorf("不能同时使用 -f 和 -m")
	}

	return nil
}

// collectAllRecipients aggregates recipients from all sources
func collectAllRecipients(to string, mt multiRecipients, mtf string) ([]string, error) {
	recipients := make([]string, 0)

	if to != "" {
		recipients = append(recipients, strings.TrimSpace(to))
	}

	if len(mt) > 0 {
		recipients = append(recipients, mt...)
	}

	if mtf != "" {
		content, err := os.ReadFile(mtf)
		if err != nil {
			return nil, fmt.Errorf("读取收件人文件失败: %w", err)
		}

		for _, line := range strings.Split(string(content), "\n") {
			line = strings.TrimSpace(line)
			if line != "" {
				recipients = append(recipients, line)
			}
		}
	}

	// Remove duplicates
	unique := make([]string, 0, len(recipients))
	seen := make(map[string]bool)
	for _, r := range recipients {
		if !seen[r] {
			seen[r] = true
			unique = append(unique, r)
		}
	}

	return unique, nil
}

// 简化：仅读取小文件内容或直接文本
func getEmailContent(fromFile, msgText string) (string, error) {
	if fromFile != "" {
		content, err := os.ReadFile(fromFile)
		if err != nil {
			return "", fmt.Errorf("读取内容文件失败: %w", err)
		}
		return string(content), nil
	}

	return strings.TrimSpace(msgText), nil
}

func main() {
	// Define command-line flags
	var (
		toEmail   = flag.String("t", "", "单个收件人邮箱")
		fromFile  = flag.String("f", "", "从文件读取邮件内容或作为附件")
		msgText   = flag.String("m", "", "直接指定邮件内容")
		subject   = flag.String("s", "默认邮件主题", "邮件主题")
		mtRecips  multiRecipients
		mtfFile   = flag.String("mtf", "", "从文件读取多个收件人（每行一个）")
		ignoreInt = flag.Bool("i", false, "忽略中断信号（Ctrl+C）")
	)

	flag.Var(&mtRecips, "mt", "添加多个收件人（可重复使用，如 -mt a@x.com -mt b@x.com）")
	flag.Parse()

	// Handle interrupt ignore
	if *ignoreInt {
		setupInterruptIgnore()
	}

	// Validate required parameters
	if err := validateRequiredFlags(*toEmail, *fromFile, *msgText, mtRecips, *mtfFile); err != nil {
		fmt.Fprintf(os.Stderr, "参数错误: %v\n", err)
		flag.Usage()
		os.Exit(1)
	}

	// Collect all recipients
	recipients, err := collectAllRecipients(*toEmail, mtRecips, *mtfFile)
	if err != nil {
		log.Fatalf("获取收件人失败: %v", err)
	}

	// 新增：处理文件大小判断（小文件作为内容，大文件作为附件）
	var content string
	var attachment string

	if *fromFile != "" {
		// 获取文件信息
		fileInfo, err := os.Stat(*fromFile)
		if err != nil {
			log.Fatalf("获取文件信息失败: %v", err)
		}

		// 判断文件大小
		if fileInfo.Size() > maxBodyFileSize {
			// 大文件：作为附件，正文显示提示信息
			attachment = *fromFile
			content = fmt.Sprintf("邮件包含附件：%s（大小：%.2fKB）", fileInfo.Name(), float64(fileInfo.Size())/1024)
		} else {
			// 小文件：作为正文内容
			content, err = getEmailContent(*fromFile, *msgText)
			if err != nil {
				log.Fatalf("获取邮件内容失败: %v", err)
			}
		}
	} else {
		// 处理直接文本内容
		content, err = getEmailContent(*fromFile, *msgText)
		if err != nil {
			log.Fatalf("获取邮件内容失败: %v", err)
		}
	}

	// Send email to each recipient (传递附件参数)
	for _, to := range recipients {
		if err := SendEmail(to, *subject, content, attachment); err != nil {
			log.Printf("向 %s 发送失败: %v", to, err)
			continue
		}
		log.Printf("向 %s 发送成功", to)
	}
}
